package com.nl.deal;

import org.apache.log4j.Logger;

import com.nl.redis.RedisCluster;
import com.nl.util.ConfigUtils;
import com.nl.util.ConnectionManager;
import com.nl.util.DateUtils;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TaskMonitor {

	private static final Logger logger = Logger.getLogger(TaskMonitor.class);

	private static final int INTERVAL ;
	private static final int ADDPERSEND ;
	private String redisCfg;
	private RedisCluster cluster;
	static{
		INTERVAL= Integer.parseInt(ConfigUtils.getInstance().getValue("monitorInterval"));
		ADDPERSEND=Integer.parseInt(ConfigUtils.getInstance().getValue("addPerSend"));
	}
	private ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

	private TaskMonitor() {
		logger.info("Initialize TaskMonitor.......");
	}

	private static class SingletonHolder {
		private static final TaskMonitor instance = new TaskMonitor();
	}

	public static TaskMonitor getInstance() {
		return SingletonHolder.instance;
	}

	public void startTaskMonitor(String redisCfg) {
		this.redisCfg = redisCfg;
		this.service.scheduleAtFixedRate(new TargetTask(), 0, INTERVAL, TimeUnit.SECONDS);
	}

	private void exceute() {
		try {
			String dealTime = getOracleDate();
			List<Map<String, Long>> list = this.gainMgrDateChange(dealTime);
			if (list.size() == 0) {
				// 总数量达到阀值要求的关闭项目,周期数据达到阀值要求的暂停项目
				Map<String, String> map = this.assembleMgrMap(checkThresholdStatus(), mgrTimeOver());
				logger.info("wait update project：" + map.toString());
				this.updateMgrToClose(map);
			} else {
				//                // 针对跨天的情况，重置响应部分数据量，开启项目
				this.resetPartMgrThreshold(list, dealTime);
			}
		} catch (Exception e) {
			logger.error("deal data error，wait next deal···");
		}
	}

	/**
	 * 查询正常运行的项目阀值设置情况
	 *
	 * @return 正常运行的项目阀值配置集合
	 * @throws SQLException
	 */
	private List<ThresholdInfo> checkThresholdStatus() throws SQLException {
		Connection conn = null;
		ResultSet rs = null;
		PreparedStatement pres = null;
		List<ThresholdInfo> list = new ArrayList<ThresholdInfo>();
		String sql = "select a.id,a.is_threshold,a.threshold,a.CYCLE,a.CYCLE_THRESHOLD ," +
				"b.total_count,b.year_count,b.quarter_count,b.month_count,b.week_count,b.day_count" +
				" from esms_mgr_template_info a,sms_realtime_control b where a.id = b.id " +
				" and a.template_state = 2 and a.mgr_state = 0 and a.is_threshold <> 0";
		try {
			conn = ConnectionManager.getInstance().getConnection();
			pres = conn.prepareStatement(sql);
			rs = pres.executeQuery();
			ThresholdInfo thresholdInfo;
			int percent = ADDPERSEND;
			while (rs.next()) {
				long totalThreshold = rs.getLong("THRESHOLD");
				long cycleThreshold= rs.getLong("CYCLE_THRESHOLD");
				if (totalThreshold > SynchDictData.thresholdControl) {
					totalThreshold = totalThreshold * SynchDictData.upPerLimit / 100;
				} else {
					totalThreshold = totalThreshold * SynchDictData.lowerPerLimit / 100;
				}

				if (cycleThreshold > SynchDictData.thresholdControl) {
					cycleThreshold = cycleThreshold * SynchDictData.upPerLimit / 100;
				} else {
					cycleThreshold = cycleThreshold * SynchDictData.lowerPerLimit / 100;
				}
				thresholdInfo = new ThresholdInfo();
				thresholdInfo.setMgrId(rs.getString("ID"));
				thresholdInfo.setIsThreshold(rs.getInt("IS_THRESHOLD"));
				thresholdInfo.setTotalThreshold(totalThreshold);
				thresholdInfo.setCycle(rs.getInt("CYCLE"));
				thresholdInfo.setCycleThreshold(cycleThreshold);
				thresholdInfo.setTotalCount(rs.getLong("TOTAL_COUNT"));
				thresholdInfo.setYearCount(rs.getLong("YEAR_COUNT"));
				thresholdInfo.setQuarterCount(rs.getLong("QUARTER_COUNT"));
				thresholdInfo.setMonthCount(rs.getLong("MONTH_COUNT"));
				thresholdInfo.setWeekCount(rs.getLong("WEEK_COUNT"));
				thresholdInfo.setDayCount(rs.getLong("DAY_COUNT"));
				//                thresholdInfo.setTimeCount(rs.getLong("TIME_COUNT"));
				logger.debug(thresholdInfo.toString());
				list.add(thresholdInfo);
			}
		} catch (SQLException e) {
			logger.error("select sms cfg threshold error");
			e.printStackTrace();
			throw e;
		} finally {
			try{
				if (null != rs) {
					rs.close();
				}

				if (null != pres) {
					pres.close();
				}

				if (null != conn) {
					conn.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return list;
	}

	/**
	 * 查询发送时间到期的项目编码
	 *
	 * @return 项目编码集合
	 */
	private List<String> mgrTimeOver() {
		Connection conn = null;
		ResultSet rs = null;
		PreparedStatement pres = null;
		List<String> list = new ArrayList<String>();
		String sql = "select a.id from esms_mgr_template_info a " +
				"where  a.mgr_state<>2 and a.end_date<=trunc(sysdate)";
		try {
			conn = ConnectionManager.getInstance().getConnection();
			pres = conn.prepareStatement(sql);
			rs = pres.executeQuery();
			while (rs.next()) {
				list.add(rs.getString("ID"));
			}
		} catch (Exception e) {
			logger.error("查询发送时间已到期的项目编码出现异常");
			e.printStackTrace();
		} finally {
			try{
				if (null != rs) {
					rs.close();
				}

				if (null != pres) {
					pres.close();
				}

				if (null != conn) {
					conn.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return list;
	}

	/**
	 * 更新项目状态
	 *
	 * @param map 项目编码和状态集合
	 * @throws SQLException
	 */
	private void updateMgrToClose(Map<String, String> map) throws SQLException {

		Connection conn = null;
		PreparedStatement pres = null;
		String sql = "update esms_mgr_template_info t set t.mgr_state = ? where t.id = ?";
		try {
			conn = ConnectionManager.getInstance().getConnection();
			pres = conn.prepareStatement(sql);
			for (String mgrId : map.keySet()) {
				pres.setString(1, map.get(mgrId));
				pres.setString(2, mgrId);
				pres.addBatch();
			}
			pres.executeBatch();
		} catch (SQLException e) {
			logger.error("更新项目状态时出现异常");
			e.printStackTrace();
			throw e;
		} finally {
			try{
				if (null != pres) {
					pres.close();
				}

				if (null != conn) {
					conn.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}



	}

	/**
	 * 拼装需改变状态的项目和状态类型
	 *
	 * @param thresholdList 到达阀值的项目信息
	 * @param overtimeList  发送时间过期的项目信息
	 * @return 项目编码和项目状态集合
	 */
	private Map<String, String> assembleMgrMap(List<ThresholdInfo> thresholdList, List<String> overtimeList) {
		Map<String, String> map = new HashMap<String, String>();

		try{
			cluster=new RedisCluster(redisCfg);
			for (String mgrId : overtimeList) {
				map.put(mgrId, "2");
			}


			for (ThresholdInfo tInfo : thresholdList) {

				String dayC=cluster.get(tInfo.getMgrId()+"dayCount");
				String weekC=cluster.get(tInfo.getMgrId()+"weekCount");
				String monthC=cluster.get(tInfo.getMgrId()+"monthCount");
				String quarterC=cluster.get(tInfo.getMgrId()+"quarterCount");
				String yearC=cluster.get(tInfo.getMgrId()+"yearCount");
				String totalC=cluster.get(tInfo.getMgrId()+"totalCount");

				if (null == dayC || "".equals(dayC)) {
					dayC = "0";
				}
				if (null == weekC || "".equals(weekC)) {
					weekC = "0";
				}
				if (null == monthC || "".equals(monthC)) {
					monthC = "0";
				}
				if (null == quarterC || "".equals(quarterC)) {
					quarterC = "0";
				}
				if (null == yearC || "".equals(yearC)) {
					yearC = "0";
				}
				if (null == totalC || "".equals(totalC)) {
					totalC = "0";
				}

				int dayCount = Integer.valueOf(dayC);
				int weekCount = Integer.valueOf(weekC);
				int monthCount = Integer.valueOf(monthC);
				int quarterCount = Integer.valueOf(quarterC);
				int yearCount = Integer.valueOf(yearC);
				int totalCount = Integer.valueOf(totalC);


				//判断发送量是否到达阀值。
				if (tInfo.getIsThreshold() == 2 || tInfo.getIsThreshold() == 3) {
					if (tInfo.getCycle() == 1 && ( yearCount - tInfo.getCycleThreshold() >= 0)
						|| tInfo.getCycle() == 4 && (dayCount - tInfo.getCycleThreshold() >= 0)
							) {
						map.put(tInfo.getMgrId(), "1");
						logger.info("-----"+tInfo.getMgrId()+"到达周期阀值，暂停操作");
					}
				}
				if ((tInfo.getIsThreshold() == 1 )
						&& (totalCount - tInfo.getTotalThreshold() >= 0)) {
					map.put(tInfo.getMgrId(), "2");
					logger.info("-----"+tInfo.getMgrId()+"到达总阀值，关闭操作");
				}
			}
		} catch (IOException e) {
			/*try {
    			cluster.close();
    		} catch (IOException e1) {
    			e1.printStackTrace();
    		}*/
			e.printStackTrace();
			logger.info("获取redis实例异常"+e.toString());
		}
		finally {
			try {
				if(null != cluster){
					cluster.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return map;
	}

	/**
	 * 针对跨天的情况，重置响应部分数据量，开启项目
	 *
	 * @param list     待处理项目集合
	 * @param dealTime 数据库时间
	 * @throws Exception
	 */
	private void resetPartMgrThreshold(List<Map<String, Long>> list, String dealTime) throws Exception {
		Connection conn = null;
		PreparedStatement pres1 = null;
		PreparedStatement pres2 = null;
		String sql1 = this.assembleSql(dealTime);
		String sql2 = "update esms_mgr_template_info t set t.mgr_state = 0 where t.id = ?";
		logger.info("resetPartMgrThreshold sql:" + sql1);
		try {
			conn = ConnectionManager.getInstance().getConnection();
			conn.setAutoCommit(false);
			pres1 = conn.prepareStatement(sql1);
			pres1.executeUpdate();

			pres2 = conn.prepareStatement(sql2);
			for (Map<String, Long> map : list) {
				logger.info("待开启的项目：" + map.toString());
				if (judgeDateConform(map.get("cycle"), dealTime, map)) {
					pres2.setLong(1, map.get("id"));
					pres2.addBatch();
				}
			}
			pres2.executeBatch();

			conn.commit();
		} catch (SQLException e) {
			logger.error("更新项目状态和项目的数据量出现异常");
			e.printStackTrace();
			throw e;
		} finally {
			try{
				if (null != pres1) {
					pres1.close();
				}

				if (null != pres2) {
					pres2.close();
				}

				if (null != conn) {
					conn.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 判断是否需要开启项目
	 *
	 * @param cycle    周期阀值类型
	 * @param dealTime 数据库时间
	 * @return 是否满足条件
	 * @throws ParseException
	 */
	private boolean judgeDateConform(long cycle, String dealTime, Map<String, Long> map) throws ParseException {
		boolean flag = false;
		Date date = DateUtils.formatDate(dealTime);
		if (cycle == 4) {
			flag = true;
			logger.info("跨日开启项目" + map.get("id"));
		} else if (cycle == 3 && DateUtils.sameDate(date,DateUtils.getFirstDayOfWeek(date))) {
			flag = true;
			logger.info("跨周开启项目" + map.get("id"));
		} else if (cycle == 2 && DateUtils.sameDate(date,DateUtils.getFirstDayOfMonth(date))) {
			flag = true;
			logger.info("跨月开启项目" + map.get("id"));
		} else if (cycle == 1 && DateUtils.sameDate(date,DateUtils.getCurrYearFirst())) {
			flag = true;
			logger.info("跨年开启项目" + map.get("id"));
		} else if (cycle == 6 && DateUtils.sameDate(date,DateUtils.getFirstDayOfQuarter(date))) {
			flag = true;
			logger.info("跨季度开启项目" + map.get("id"));
		}
		return flag;
	}

	/**
	 * 拼装更新实时提交量的sql，重置提交量
	 *
	 * @param dealTime 数据库时间
	 * @return sql
	 * @throws ParseException
	 */
	private String assembleSql(String dealTime) throws ParseException {
		StringBuffer buffer = new StringBuffer();
		buffer.append("update sms_realtime_control t set t.day_count = 0");
		Boolean rsetRealsend = this.rsetRealsend("dayCount","0");
		Date date = DateUtils.formatDate(dealTime);
		if (DateUtils.sameDate(date, DateUtils.getFirstDayOfWeek(date))) {
			buffer.append(",t.week_count = 0");
			this.rsetRealsend("weekCount","0");
		}
		if (DateUtils.sameDate(date,DateUtils.getFirstDayOfMonth(date))) {
			buffer.append(",t.month_count = 0");
			this.rsetRealsend("monthCount","0");
		}
		if (DateUtils.sameDate(date, DateUtils.getFirstDayOfQuarter(date))) {
			buffer.append(",t.quarter_count = 0");
			this.rsetRealsend("quarterCount","0");
		}
		if (DateUtils.sameDate(date,DateUtils.getCurrYearFirst())) {
			buffer.append(",t.year_count = 0");
			this.rsetRealsend("yearCount","0");
		}
		buffer.append(",t.flag_time = sysdate");//跨天所有的都重置
		return buffer.toString();
	}

	/**
	 * 查询非关闭、模板审核通过、且设置了周期阀值的项目是否跨天
	 *
	 * @param dealTime 数据库时间
	 * @throws SQLException
	 */
	private List<Map<String, Long>> gainMgrDateChange(String dealTime) throws SQLException {
		Connection conn = null;
		ResultSet rs = null;
		PreparedStatement pres = null;
		List<Map<String, Long>> list = new ArrayList<Map<String, Long>>();
		String sql = "select a.id,a.cycle from esms_mgr_template_info a, sms_realtime_control b " +
				"where a.id = b.id  and a. template_state= 2  and a.mgr_state in (0,1) and a.is_threshold " +
				"in (2, 3) and a.cycle <> 5 and trunc(b.flag_time) <> to_date(?,'yyyyMMdd')";
		logger.debug("gainMgrDateChange sql:" + sql);
		try {
			conn = ConnectionManager.getInstance().getConnection();
			pres = conn.prepareStatement(sql);
			pres.setString(1, dealTime.substring(0, 8));
			rs = pres.executeQuery();
			Map<String, Long> map;
			while (rs.next()) {
				map = new HashMap<String, Long>();
				map.put("id", rs.getLong("ID"));
				map.put("cycle", rs.getLong("CYCLE"));
				logger.debug("跨天开启的项目：" + map.toString());
				list.add(map);
			}
		} catch (SQLException e) {
			logger.error("查询非关闭、模板审核通过、且设置了周期阀值的跨天项目出现异常");
			e.printStackTrace();
			throw e;
		} finally {
			try{
				if (null != rs) {
					rs.close();
				}

				if (null != pres) {
					pres.close();
				}

				if (null != conn) {
					conn.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return list;
	}

	/**
	 * 获取数据库时间
	 * @throws Exception 
	 */
	private String getOracleDate() throws Exception{
		String strTime = "";
		Connection conn = null;
		ResultSet rs = null;
		PreparedStatement pres = null;
		String sql = "select to_char(sysdate,'yyyyMMdd hh24:mi:ss') tim from dual";
		try {
			conn = ConnectionManager.getInstance().getConnection();
			pres = conn.prepareStatement(sql);
			rs = pres.executeQuery();
			if(rs.next()){
				strTime = rs.getString("TIM");
			}
		} catch (Exception e) {
			logger.error("查询数据库时间出现异常···");
			throw e;
		} finally {
			try{
				if (null != rs) {
					rs.close();
				}

				if (null != pres) {
					pres.close();
				}

				if (null != conn) {
					conn.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return strTime;
	}
	
	private Boolean rsetRealsend(String thresoldName,String num){
    	boolean result = false;
    	Connection conn = null;
		ResultSet rs = null;
		PreparedStatement pres = null;
		String mgrId="";
		String sql = "select id from sms_realtime_control ";
		int reSetMgrcount=0;    	
    	try {
    		conn = ConnectionManager.getInstance().getConnection();
			pres = conn.prepareStatement(sql);
			rs = pres.executeQuery();
			cluster=new RedisCluster(redisCfg);	 
			while(rs.next()){
				mgrId = rs.getString("id");
		        cluster.set(mgrId+thresoldName, num);
		        logger.info("跨周期项目发送量重置成功，项目编码："+mgrId);
		        reSetMgrcount++;
			} 
			logger.info("完成发送量重置的跨周期项目数量："+reSetMgrcount);
			result = true;
		} catch (Exception e) {
	        logger.error("跨周期项目发送量重置出现异常，项目编码："+mgrId+";error:"+e.toString());
			e.printStackTrace();
		}finally {
			try{
				if(null != cluster){
		        	cluster.close();
		        }
        		if (null != rs) {
            		rs.close();
            	}       		
        		if (null != pres) {
        			pres.close();
            	}
        		if (null != conn) {
        			conn.close();
            	}
        	} catch (Exception e) {
        		logger.error("close--error:"+e.toString());
        		e.printStackTrace();
        	}
		}
    	return result;
    	
    }

	private class TargetTask implements Runnable {

		@Override
		public void run() {
			exceute();
			logger.info("状态控制线程开始执行，时间：" + new Date());
		}
	}
}
